/* src/interfaces/ecpg/ecpglib/memory.c */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include "ecpg-pthread-win32.h"
#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"

void ecpg_free(void *fc_ptr)
{
	free(fc_ptr);
}

char * ecpg_alloc(long fc_size, int fc_lineno)
{
	char	   *fc_new = (char *) calloc(1L, fc_size);

	if (!fc_new)
	{
		ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return NULL;
	}

	return fc_new;
}

char * ecpg_realloc(void *fc_ptr, long fc_size, int fc_lineno)
{
	char	   *fc_new = (char *) realloc(fc_ptr, fc_size);

	if (!fc_new)
	{
		ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return NULL;
	}

	return fc_new;
}

char * ecpg_strdup(const char *fc_string, int fc_lineno)
{
	char	   *fc_new;

	if (fc_string == NULL)
		return NULL;

	fc_new = strdup(fc_string);
	if (!fc_new)
	{
		ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return NULL;
	}

	return fc_new;
}

/* 保持用户分配的内存列表 */
struct auto_mem
{
	void	   *pointer;
	struct auto_mem *next;
};

#ifdef ENABLE_THREAD_SAFETY
static pthread_key_t auto_mem_key;
static pthread_once_t auto_mem_once = PTHREAD_ONCE_INIT;

static void fc_auto_mem_destructor(void *fc_arg)
{
	(void) fc_arg;					/* 让编译器安静 */
	ECPGfree_auto_mem();
}

static void fc_auto_mem_key_init(void)
{
	pthread_key_create(&auto_mem_key, fc_auto_mem_destructor);
}

static struct auto_mem *
fc_get_auto_allocs(void)
{
	pthread_once(&auto_mem_once, fc_auto_mem_key_init);
	return (struct auto_mem *) pthread_getspecific(auto_mem_key);
}

static void fc_set_auto_allocs(struct auto_mem *fc_am)
{
	pthread_setspecific(auto_mem_key, fc_am);
}
#else
static struct auto_mem *auto_allocs = NULL;

#define get_auto_allocs()		(auto_allocs)
#define set_auto_allocs(am)		do { auto_allocs = (am); } while(0)
#endif

char * ecpg_auto_alloc(long fc_size, int fc_lineno)
{
	void	   *fc_ptr = (void *) ecpg_alloc(fc_size, fc_lineno);

	if (!fc_ptr)
		return NULL;

	if (!ecpg_add_mem(fc_ptr, fc_lineno))
	{
		ecpg_free(fc_ptr);
		return NULL;
	}
	return fc_ptr;
}

bool ecpg_add_mem(void *fc_ptr, int fc_lineno)
{
	struct auto_mem *fc_am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), fc_lineno);

	if (!fc_am)
		return false;

	fc_am->pointer = fc_ptr;
	fc_am->next = fc_get_auto_allocs();
	fc_set_auto_allocs(fc_am);
	return true;
}

void ECPGfree_auto_mem(void)
{
	struct auto_mem *fc_am = fc_get_auto_allocs();

	/*
		 * 如果连接中断，我们可能会来到这里，所以我们检查
		 * 这个，而不是仅仅给出通用的内部错误
		 */
	if (fc_am)
	{
		do
		{
			struct auto_mem *fc_act = fc_am;

			fc_am = fc_am->next;
			ecpg_free(fc_act->pointer);
			ecpg_free(fc_act);
		} while (fc_am);
		fc_set_auto_allocs(NULL);
	}
}

void ecpg_clear_auto_mem(void)
{
	struct auto_mem *fc_am = fc_get_auto_allocs();

	/* 只释放我们自己的结构 */
	if (fc_am)
	{
		do
		{
			struct auto_mem *fc_act = fc_am;

			fc_am = fc_am->next;
			ecpg_free(fc_act);
		} while (fc_am);
		fc_set_auto_allocs(NULL);
	}
}
